import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 
 */

/**
 * @author thuvh
 * 
 */
public class Transformer implements Constants {
	static BufferedReader sourceTemplateReader = null;
	static BufferedReader targetTemplateReader = null;
	static BufferedReader ruleTableReader = null;
	static int k = 0;

	/**
	 * get extension of file
	 * 
	 * @param filename
	 *            - name of file
	 * @return the extension of the file
	 */
	static String getExtension(String filename) {
		int lastDotIndex = filename.lastIndexOf(".");

		return filename.substring(lastDotIndex + 1);
	}

	static BufferedReader getBufferedReader(InputStream is)
			throws UnsupportedEncodingException {
		return new BufferedReader(new InputStreamReader(is, DEFAULT_ENCODING));
	}

	static BufferedReader getBufferedReader(String filename) throws IOException {
		if (getExtension(filename).equals("gz")) {
			return getBufferedReader(new GZIPInputStream(new FileInputStream(
					filename)));
		}
		return getBufferedReader(new FileInputStream(filename));
	}

	static BufferedWriter getBufferedWriter(OutputStream os)
			throws UnsupportedEncodingException {
		return new BufferedWriter(new OutputStreamWriter(os, DEFAULT_ENCODING));
	}

	static BufferedWriter getBufferedWriter(String filename, boolean append)
			throws UnsupportedEncodingException, FileNotFoundException,
			IOException {
		if (getExtension(filename).equals("gz")) {
			return getBufferedWriter(new GZIPOutputStream(new FileOutputStream(
					filename, append)));
		}
		return getBufferedWriter(new FileOutputStream(filename, append));
	}

	static String getRule(String sourceSentence, String targetSentence) {
		StringBuffer targetStringBuffer = new StringBuffer();
		StringBuffer sourceStringBuffer = new StringBuffer();

		int targetIndex = 0;
		int sourceIndex = 0;

		int numOfTargetNonTerminal = 0;
		int numOfSourceNonTerminal = 0;

		Map<Integer, Integer> sourceNonTerminalIndex = new HashMap<Integer, Integer>();
		Map<Integer, Integer> targetNonTerminalIndex = new HashMap<Integer, Integer>();

		for (int i = 0; i < targetSentence.length();) {
			if (targetSentence.charAt(i) == '{') {

				int iClosed = targetSentence.indexOf("}", i);
				if (iClosed == -1) {
					return null;
				}
				if (targetSentence.charAt(i + 2) != 'w'
						&& targetSentence.charAt(i + 2) != 'W'
						&& targetSentence.charAt(i + 1) != ' '
						&& targetSentence.charAt(iClosed - 1) != ' ') {
					return null;
				}
				String key = targetSentence.substring(i + 3, iClosed - 1);
				// targetStringBuffer.replace(i, iClosed, MOSES_NON_TERMINAL2);
				targetStringBuffer.append(MOSES_NON_TERMINAL2);
				targetNonTerminalIndex.put(Integer.parseInt(key), targetIndex);
				i = iClosed + 1;
				numOfTargetNonTerminal++;
			} else if (targetSentence.charAt(i) == ' ') {
				i++;
				targetStringBuffer.append(' ');
				targetIndex++;
				while (i < targetSentence.length()
						&& targetSentence.charAt(i) == ' ') {
					i++;
				}
			} else {
				targetStringBuffer.append(targetSentence.charAt(i));
				i++;
			}
		}

		if (numOfTargetNonTerminal == 0) {
			return null;
		}

		for (int i = 0; i < sourceSentence.length();) {
			if (sourceSentence.charAt(i) == '{') {
				int iClosed = sourceSentence.indexOf("}", i);
				if (iClosed == -1) {
					return null;
				}
				if (sourceSentence.charAt(i + 2) != 'w'
						&& sourceSentence.charAt(i + 2) != 'W'
						&& sourceSentence.charAt(i + 1) != ' '
						&& sourceSentence.charAt(iClosed - 1) != ' ') {
					return null;
				}
				String key = sourceSentence.substring(i + 3, iClosed - 1);
				// sourceStringBuffer.replace(i, iClosed, MOSES_NON_TERMINAL2);
				sourceStringBuffer.append(MOSES_NON_TERMINAL2);
				sourceNonTerminalIndex.put(Integer.parseInt(key), sourceIndex);
				i = iClosed + 1;
				numOfSourceNonTerminal++;
			} else if (sourceSentence.charAt(i) == ' ') {
				i++;
				sourceStringBuffer.append(' ');
				sourceIndex++;
				while (i < sourceSentence.length()
						&& sourceSentence.charAt(i) == ' ') {
					i++;
				}
			} else {
				sourceStringBuffer.append(sourceSentence.charAt(i));
				i++;
			}
		}

		if (numOfSourceNonTerminal != numOfTargetNonTerminal) {
			return null;
		}

		String alignmentInfo = getAlignmentInfo(sourceNonTerminalIndex,
				targetNonTerminalIndex);
		if (alignmentInfo == null) {
			return null;
		}

		double pFE = (k == 0) ? 1.0 : k
				* (targetIndex + numOfTargetNonTerminal);
		double pEF = (k == 0) ? 1.0 : k
				* (sourceIndex + numOfSourceNonTerminal);
		return String.format(MOSES_PATTERN, sourceStringBuffer.toString(),
				targetStringBuffer.toString(), alignmentInfo, pFE, pEF);
		// return "";
	}

	private static Map<Integer, Integer> getSortedMap(
			Map<Integer, Integer> unsortedMap) {
		LinkedList<Map.Entry<Integer, Integer>> list = new LinkedList<Map.Entry<Integer, Integer>>(
				unsortedMap.entrySet());
		if (list.size() == 1) {
			return unsortedMap;
		}

		Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {

			@Override
			public int compare(Entry<Integer, Integer> arg0,
					Entry<Integer, Integer> arg1) {

				return arg0.getValue().compareTo(arg1.getValue());
			}

		});

		Map<Integer, Integer> sortedMap = new HashMap<Integer, Integer>();

		for (Iterator<Entry<Integer, Integer>> it = list.iterator(); it
				.hasNext();) {
			Map.Entry<Integer, Integer> entry = it.next();
			sortedMap.put(entry.getKey(), entry.getValue());
		}

		return sortedMap;
	}

	public static String getAlignmentInfo(
			Map<Integer, Integer> sourceNonTerminalIndex,
			Map<Integer, Integer> targetNonTerminalIndex) {
		StringBuffer sb = new StringBuffer();
		Map<Integer, Integer> sortedTargetNonTerminalIndex = getSortedMap(targetNonTerminalIndex);
		for (Map.Entry<Integer, Integer> entry : sortedTargetNonTerminalIndex
				.entrySet()) {
			Integer key = entry.getKey();
			Integer source = sourceNonTerminalIndex.get(key);
			Integer target = entry.getValue();
			if (source == null) {
				return null;
			}
			sb.append(source.toString() + "-" + target.toString() + " ");
		}

		return sb.toString().trim();
	}

	public static void main2(String[] args) throws IOException {
		String outputFileName = "rule-table.gz";
		String inputFileName = null;
		String f = null;
		String e = null;
		String sourceInputFileName = null;
		String targetInputFileName = null;
		boolean append = false;

		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-i")) {
				i++;
				if (args.length == i) {
					usage();
				}

				inputFileName = args[i];
			} else if (args[i].equals("-f")) {
				i++;
				if (args.length == i) {
					usage();
				}

				f = args[i];
			} else if (args[i].equals("-e")) {
				i++;
				if (args.length == i) {
					usage();
				}

				e = args[i];
			} else if (args[i].equals("-o")) {
				i++;
				if (args.length == i) {
					usage();
				}

				outputFileName = args[i];
			} else if (args[i].equals("-a")) {
				append = true;
			} else if (args[i].equals("-k")) {
				i++;
				if (args.length == i) {
					usage();
				}

				try {
					k = Integer.parseInt(args[i]);
					if (k < 0) {
						usage();
					}
				} catch (NumberFormatException nfe) {
					usage();
				}
			} else {
				usage();
			}
		}

		if (f == null || e == null || inputFileName == null) {
			usage();
		}

		sourceInputFileName = inputFileName + "." + f;
		targetInputFileName = inputFileName + "." + e;
		BufferedReader sourceBufferedReader = getBufferedReader(sourceInputFileName);
		BufferedReader targetBufferedReader = getBufferedReader(targetInputFileName);

		BufferedWriter outputWriter = getBufferedWriter(outputFileName, append);
		BigInteger bi = new BigInteger("0");
		while (true) {
			String sourceSent = sourceBufferedReader.readLine();
			String targetSent = targetBufferedReader.readLine();

			if (sourceSent == null && targetSent == null) {
				break;
			} else if (sourceSent == null || targetSent == null) {
				throw new IOException("two files are not match!!!");
			}
			// System.out.println(sourceSent + " ||| " + targetSent);
			String rule = getRule(sourceSent, targetSent);
			if (rule != null) {
				outputWriter.write(rule + "\n");

			} else {
				System.err.println(bi.toString() + " ||| " + sourceSent
						+ " ||| " + targetSent);
			}
			bi = bi.add(new BigInteger("1"));
		}

		sourceBufferedReader.close();
		targetBufferedReader.close();
		outputWriter.close();
	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		main2(args);
		// String left = "kiểm_tra tính hợp_lệ của tham_số { W1 }";
		// // String left = "{ w1 } の 指定 が ない 場合 は 、 { w2 } と する";
		// String right = "パラメータ [ { w1 } ] の の 妥当 性 を チェック する";
		// // String right = "{ w1 } thuộc_phạm_vi { w2 }";
		// String rule = getRule(right, left);
		// System.out.println(rule);
	}

	public static void usage() {
		System.out.println("usage: -i <file_input> " + "-f source_language "
				+ "-e target_language " + "[-o output_file] [-k number]");
		System.exit(1);
	}
}
